#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
//#include <sys/socket.h> //这三个是 string _ip转化为IPv4整型的
//#include <netinet/in.h>
#include <arpa/inet.h> //还有htons
#include "log.hpp"

using func_t = std::function<std::string(const std::string&)>;

Log lg; //声明日志功能
//已经有宏定义了size

enum{
    USAGE_ERR = 1,
    SOCKER_ERR,
    BIND_ERR,
    POPEN_ERR
};

class serve
{
public:
    serve(uint16_t port = 8888, const std::string& ip = "0.0.0.0")
    :_sockfd(-1), _ip(ip), _port(port), _isrunning(false)
    {}
    ~serve()
    {
        close(_sockfd);
    }

    void init() //初始化服务器的终极目标是：bind端口号
    {
        //1、创建socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); //IPv4 面向数据流 协议0
        if(_sockfd < 0) //创建失败
        {
            lg(Fatal, "create socket fail, erron code: %d, reason: %s", errno, strerror(errno));
            exit(SOCKER_ERR);
        }
        //创建成功
        lg(Info, "create socket success, fd:%d", _sockfd);
        
        // 2、建立服务端sockaddr结构体 —> bind需要
        struct sockaddr_in local; //创建一个本地 头文件需要：<sys/socket.h>和<netinet/in.h>
        memset(&local, 0, sizeof(local)); //全部字段初始化为0
        //填写local中的字段 —> 注意网络字节流要求“大端”
        local.sin_family = AF_INET; //表示地址族
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); //自动转化为大端,且注意需要多个访问成员
        // local.sin_addr.s_addr = htonl(INADDR_ANY); 
        //一般服务器的_ip需要接受来自任意ip的数据，可能一台主机有多个ip
        //默认_ip为0，就是接受ip数据
        local.sin_port = htons(_port); //转化为大端

        // 3、开始bind
        int bind_ret = bind(_sockfd, (const sockaddr*)&local, sizeof(local));
        if(bind_ret < 0)
        {
            lg(Fatal, "bind fail, errno:%d, reason:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind success");
        

    }
    
    void run(func_t func) // 传入一个方法，实现处理数据和拿数据的解耦
    {
        _isrunning = true;
        char buffer[SIZE] = { 0 };
        while (_isrunning)
        {
            //从客户端读取数据
            struct sockaddr_in client;
            //读取客服端数据
            socklen_t len = sizeof(client);
            ssize_t recv_ret = recvfrom(_sockfd, buffer, SIZE - 1, 0, (struct sockaddr *)&client, &len);
            if(recv_ret < 0) 
            {
                //创建失败
                lg(Warning, "serve get a message fail, erron: %d, reason: %s", errno, strerror(errno));
                sleep(3);
                //continue;
            }
            buffer[recv_ret] = 0; //'\0'的设置
            std::string in = buffer;
            std::string echo_str = func(in); //处理得到的信息
            sendto(_sockfd, echo_str.c_str(), echo_str.size(), 0, (const struct sockaddr*)&client, len); //将信息返回
        }

    }

private:
    //网络请求
    int _sockfd;     // 网络套接字文件描述符
    std::string _ip; // 任意地址bind 0
    uint16_t _port;  // 表明服务器进程的端口号
    bool _isrunning; //运行状态
};